﻿#include "HttpDownloader.h"
#include "Downloader.h"
#include "DownloadController.h"
#include <QNetworkRequest>
#include <QNetworkProxy>
#include <QFile>
#include <QDebug>
#include <mutex>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <sstream>
#include <string>
#include <openssl/sha.h>

std::mutex filemutex;
//const int       m_Index;              // 下载器编号，控制器用于区分不同线程的下载器
HttpDownloader::HttpDownloader(int startPoint, int endPoint ,int index, int speed,QObject *parent )
              : QObject(parent)//Download(parent)
{
    m_StartPoint = startPoint;
    m_EndPoint = endPoint;
    m_Index =index;
    m_speed = speed;
    m_HaveDoneBytes = 0;
    m_CurrentRecvBytes = 0;
qDebug() << "HttpDownloader index "<<m_Index;
}

HttpDownloader::~HttpDownloader()
{
qDebug() << "ParentClass destructor called";
}

// 获取当前下载进度
void HttpDownloader::getCurrentDownProgress(int& currentSize, int& totalSize)
{
    currentSize = m_HaveDoneBytes;
    totalSize = m_EndPoint - m_StartPoint;
}

// 获取当前状态
HttpDownloader::DownloadStatus HttpDownloader::getCurrentStatus(void)
{
    return m_status;
}

// 开始分片下载
void HttpDownloader::startDownload(const QUrl url)
{
    m_Url = url;
    m_Qnam = new QNetworkAccessManager;

    //qDebug() << "Part " << m_Index << ":" << m_StartPoint << ", " << m_EndPoint <<  "download";

    //根据HTTP协议，写入RANGE头部，说明请求文件的范围
    QNetworkRequest qheader;
    qheader.setUrl(url);
    qheader.setRawHeader("User-Agent", "QtApp-HttpDownLoader");
    qheader.setRawHeader("Accept", "application/octet-stream");

    /*//限速，但不能起作用
    qheader.setAttribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute, 1);
    qheader.setAttribute(QNetworkRequest::DownloadBufferAttribute, 1);
    qheader.setAttribute(QNetworkRequest::SynchronousRequestAttribute, false);
    */

    //获取的文件范围
    QString range;
    range.sprintf("bytes=%lld-%lld", m_StartPoint, m_EndPoint);
    qheader.setRawHeader("Range", range.toUtf8());

    //开始下载
    m_Reply = m_Qnam->get(qheader);
    connect(m_Reply, SIGNAL(readyRead()),this, SLOT(onReplyReadyRead()));
    //connect(m_Reply, SIGNAL(finished()),this, SLOT(onReplyFinished()));

    //开启限速检测
    if(m_speed != 0){
        m_pTimer = new QTimer(this);
        Download::connect(m_pTimer, SIGNAL(timeout()), this, SLOT(onTimeout()));
        //每100ms检测一下速度
        m_pTimer->start(100);
    }
}

 void HttpDownloader::onStart(QUrl url, QFile* file)
{

    m_File = file;
    //Hash=hash;
    startDownload(url);
}

// 暂停所有线程下载
 void HttpDownloader::onStop()
{
   if(m_status==t_Finished)
       return ;
    m_status = t_Pause;
    qDebug() << m_Index << ", " << "Pause";
    m_Reply->close();
    m_Reply->deleteLater();
    m_mutex.lock();
    m_condition.wait(&m_mutex);
//    std::unique_lock<std::mutex> lk(m_mutex);
//    m_condition.wait(lk);
    //qDebug() << m_Index << ", " << "restart";
    m_mutex.unlock();
    onRestart();
    m_status = t_Downloading;

}

  //计算文件的哈希值
 std::string HttpDownloader:: calculateFileHash(const std::string& fileName)
 {
     // 打开二进制模式的文件流
    std::ifstream file(fileName, std::ios::binary);
      if (!file.is_open()) {
          std::cerr << "Failed to open file: " << fileName << std::endl;
           return "";
         }
     // 初始化 SHA256
     SHA256_CTX sha256;
     SHA256_Init(&sha256);

     const int bufferSize = 16384;
     char buffer[bufferSize];

     // 循环读取文件内容，并更新哈希值
     while (file.read(buffer, bufferSize)) {
         SHA256_Update(&sha256, buffer, bufferSize);
     }
     // 处理剩余的数据，更新哈希值
     SHA256_Update(&sha256, buffer, file.gcount());
     // 计算最终的哈希值
     unsigned char hash[SHA256_DIGEST_LENGTH];
     SHA256_Final(hash, &sha256);

     // 将哈希值转换为字符串
     std::stringstream stream;
     //十六进制输出，不够两位数时用 0 填充
     stream << std::hex << std::setfill('0');
     //每个字节转换为两位十六进制字符串表示形式
     for (unsigned char c : hash) {
         stream << std::setw(2) << static_cast<int>(c);
     }
     // 返回哈希值字符串
     return stream.str();
 }

void HttpDownloader::onRestart()
{

    //根据HTTP协议，写入RANGE头部，说明请求文件的范围
    QNetworkRequest qheader;
    qheader.setUrl(m_Url);
    qheader.setRawHeader("User-Agent", "QtApp-HttpDownLoader");
    qheader.setRawHeader("Accept", "application/octet-stream");
    //获取的文件范围
    QString range;
    range.sprintf("bytes=%lld-%lld", m_StartPoint + m_HaveDoneBytes, m_EndPoint);
    //qDebug() << m_Index << " startDownload: " << range;
    qheader.setRawHeader("Range", range.toUtf8());

    //开始下载
    m_Reply = m_Qnam->get(qheader);
    connect(m_Reply, SIGNAL(readyRead()),this, SLOT(onReplyReadyRead()));
}

// 每100MS唤醒一次
void HttpDownloader::onTimeout(void)
{
    if(m_status==t_Finished)
        return ;
    int delta = m_HaveDoneBytes - m_CurrentRecvBytes;
    m_CurrentRecvBytes = m_HaveDoneBytes;

    //超过限制速度，休眠
    if(delta > m_speed/10)
    {
        std::int32_t temp = (((delta - m_speed/10)/ (std::int32_t)m_speed)*2000);
        m_Reply->close();
        m_Reply->deleteLater();
        std::this_thread::sleep_for(std::chrono::milliseconds(temp));

        QNetworkRequest qheader;
        qheader.setUrl(m_Url);
        qheader.setRawHeader("User-Agent", "QtApp-HttpDownLoader");
        qheader.setRawHeader("Accept", "application/octet-stream");

        QString range;
        range.sprintf("bytes=%lld-%lld", m_StartPoint + m_HaveDoneBytes, m_EndPoint);
        qheader.setRawHeader("Range", range.toUtf8());
        m_Reply = m_Qnam->get(qheader);
        connect(m_Reply, SIGNAL(readyRead()),this, SLOT(onReplyReadyRead()));
    }
}
//同步线程写入文件
void HttpDownloader::writeFile(QByteArray* data)
{
    //函数解锁自动解锁，RAII 技术
   std::lock_guard<std::mutex> lock(filemutex);
   std::int64_t countSize=m_HaveDoneBytes+data->size();
    //计算文件预期哈希值
    Hash->addData(*data);
    //写入文件
    m_File->seek(m_StartPoint + m_HaveDoneBytes);
    m_File->write(*data);
    m_File->flush();
    //将线程已下载字节数记录到文档
    saveDownload((m_Index-1)*2+5,QString::number(m_StartPoint +countSize).toStdString(),  data->size());
}
//读取数据
void HttpDownloader::onReplyReadyRead()
{
    if(!m_File)
        return;
    QByteArray* data =new QByteArray();
    //每次读取16384个字节(大小是动态的，受内存大小影响)
     *data=m_Reply->readAll();
    //qDebug() << "Part" << byteArray.size();
    if (data->size() <= 0)
          return;
    //写入文件
    writeFile(data);

    m_HaveDoneBytes += data->size();

    if(m_HaveDoneBytes + m_StartPoint == m_EndPoint || m_HaveDoneBytes + m_StartPoint - 1 == m_EndPoint || m_HaveDoneBytes + m_StartPoint + 1 == m_EndPoint)
    {
        qDebug() << "Part" << m_Index << "finshed download ";
        onReplyFinished();
    }
    emit updateCurrentSize(m_Index, data->size());
    delete data;
}

// 要编辑的行数，从1开始计数,"countSizer" 已下载总数据,new_star每个线程已下载数据
void HttpDownloader::saveDownload(int line_to_edit,std::string new_star,std::int64_t countSize)
{
        // 读取文件中的所有数据
        std::vector<std::string> lines;
        std::ifstream fin(m_outputFile);
        if (fin) {
            std::string line;
            while (getline(fin, line)) {
                lines.push_back(line);
            }
            fin.close();
        } else {
            qDebug() << "Failed to open file: " ;

        }
        // 检查要编辑的行数是否合法
        if (line_to_edit < 1 || line_to_edit > lines.size()) {
            qDebug() << "Invalid line number: " ;

        }
        // 在指定行删除旧数据，插入新数据
        lines[line_to_edit - 1] = new_star;
        std::int64_t tmp=countSize+std::stoi(lines[3]);

        lines[3]=QString::number(tmp).toStdString();
        // 将修改后的数据写回文件
        std::ofstream fout(m_outputFile);
        if (fout) {
            for (const std::string& line : lines) {
                fout << line << '\n';
            }
            fout.close();
           // qDebug() << "File updated successfully!";
        } else {
            //qDebug() << "Failed to open file: " ;

        }
}

void HttpDownloader::onReplyFinished()
{


    m_status=t_Finished;
    filemutex.lock();
    m_File->flush();
    filemutex.unlock();

    m_File = nullptr;
    m_Reply->deleteLater();
    qDebug() << "Part" << m_Index << "download finished";
    emit DownloadFinished(m_Index);


}


